home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 3 / Cream of the Crop 3.iso / comm / wnos5src.zip / SPAWN.ASM < prev    next >
Assembly Source File  |  1993-08-09  |  68KB  |  2,088 lines

  1. ;
  2. ;       --- Version 3.0 91-05-27 17:56 ---
  3. ;
  4. ;       SPAWN.ASM - Main function for memory swapping spawn call.
  5. ;
  6. ;       Public Domain Software written by
  7. ;               Thomas Wagner
  8. ;               Ferrari electronic GmbH
  9. ;               Beusselstrasse 27
  10. ;               D-1000 Berlin 21
  11. ;               Germany
  12. ;
  13. ;
  14. ; Assemble with
  15. ;
  16. ; tasm  /DPASCAL spawn,spawnp           - Turbo Pascal (Tasm only), near
  17. ; tasm  /DPASCAL /DFARCALL spawn,spawnp - Turbo Pascal (Tasm only), far
  18. ; ?asm  spawn;                          - C, default model (small)
  19. ; ?asm  /DMODL=large spawn              - C, large model
  20. ;
  21. ;       NOTE:   For C, change the 'model' directive below according to your
  22. ;               memory model, or define MODL=xxx on the command line.
  23. ;
  24. ;               For Turbo C Huge model, you must give /DTC_HUGE on the
  25. ;               command line, or define it here.
  26. ;
  27. ;
  28. ; Main function:
  29. ;
  30. ;   PASCAL:
  31. ;       function do_spawn (swapping: integer;
  32. ;                          execfname: string;
  33. ;                          cmdtail: string;
  34. ;                          envlen: word;
  35. ;                          var envp)
  36. ;
  37. ;   C:
  38. ;       int do_spawn (int swapping,
  39. ;                     char *execfname,
  40. ;                     char *cmdtail,
  41. ;                     unsigned envlen,
  42. ;                     char *envp)
  43. ;
  44. ;   Parameters:
  45. ;
  46. ;       swapping - swap/spawn/exec function:
  47. ;                       < 0: Exec, don't swap
  48. ;                         0: Spawn, don't swap
  49. ;                       > 0: Spawn, swap
  50. ;                            in this case, prep_swap must have
  51. ;                            been called beforehand (see below).
  52. ;
  53. ;       cmdtail - command tail for EXEC.
  54. ;
  55. ;       execfname - name and path of file to execute.
  56. ;
  57. ;       envlen - length of environment copy (may be 0).
  58. ;
  59. ;       envp -  pointer to environment block (must be aligned on
  60. ;               paragraph boundary). Unused if envlen is 0.
  61. ;
  62. ;       'cmdtail' and 'execfname' must be zero terminated, even when
  63. ;       calling from Pascal. For Pascal, the length byte of the string
  64. ;       is ignored.
  65. ;
  66. ;   Returns:
  67. ;       0000..00ff:     Returncode of EXECed program
  68. ;       03xx:           DOS-Error xx calling EXEC
  69. ;       0500:           Swapping requested, but prep_swap has not
  70. ;                       been called or returned an error
  71. ;       0501:           MCBs don't match expected setup
  72. ;       0502:           Error while swapping out
  73. ;
  74. ;
  75. ; For swapping, the swap method must be prepared before calling do_spawn.
  76. ;
  77. ;   PASCAL:
  78. ;       function prep_swap (method: word; swapfname: string): integer;
  79. ;   C:
  80. ;       int prep_swap (unsigned method, char *swapfname)
  81. ;
  82. ;   Parameters:
  83. ;
  84. ;       method  - bit-map of allowed swap devices:
  85. ;                       01 - Allow EMS
  86. ;                       02 - Allow XMS
  87. ;                       04 - Allow File swap
  88. ;                       10 - Try XMS first, then EMS
  89. ;                       40 - Create file as "hidden"
  90. ;                       80 - Use "create temp" call for file swap
  91. ;                      100 - Don't preallocate file
  92. ;                      200 - Check for Network, don't preallocate if net
  93. ;                     4000 - Environment block will not be swapped
  94. ;
  95. ;       swapfname - swap file name (may be undefined if the
  96. ;                   "method" parameters disallows file swap).
  97. ;                   The string must be zero terminated, even
  98. ;                   when calling from Pascal. For Pascal, the
  99. ;                   length byte of the string is ignored.
  100. ;
  101. ;   Returns:
  102. ;
  103. ;       A positive integer on success:
  104. ;               1 - EMS swap initialized
  105. ;               2 - XMS swap initialized
  106. ;               4 - File swap initialized
  107. ;       A negative integer on failure:
  108. ;               -1 - Couldn't allocate swap space
  109. ;               -2 - The spawn module is located too low in memory
  110. ;
  111. ;
  112.         IFDEF   PASCAL
  113.         .model  tpascal
  114. ;
  115.         extrn   prefixseg: word
  116. ;
  117. ptrsize =       1
  118.         ELSE
  119. ;        IFNDEF  MODL
  120. ;        .model  small,c
  121. ;        ELSE
  122. ;%       .model  MODL,c
  123. ;        ENDIF
  124.          .model large,c
  125. ;
  126. ptrsize =       @DataSize
  127. ;
  128.         extrn   _psp: word
  129.         ENDIF
  130. ;
  131.         public  do_spawn
  132.         public  prep_swap
  133. ;
  134. stacklen        =       256             ; local stack
  135. ;
  136. ;       "ems_size" is the EMS block size: 16k.
  137. ;
  138. ems_size        =       16 * 1024       ; EMS block size
  139. ems_parasize    =       ems_size / 16   ; same in paragraphs
  140. ems_shift       =       10              ; shift factor for paragraphs
  141. ems_paramask    =       ems_parasize-1  ; block mask
  142. ;
  143. ;       "xms_size" is the unit of measurement for XMS: 1k
  144. ;
  145. xms_size        =       1024            ; XMS block size
  146. xms_parasize    =       xms_size / 16   ; same in paragraphs
  147. xms_shift       =       6               ; shift factor for paragraphs
  148. xms_paramask    =       xms_parasize-1  ; block mask
  149. ;
  150. ;       Method flags
  151. ;
  152. USE_EMS         =       01h
  153. USE_XMS         =       02h
  154. USE_FILE        =       04h
  155. XMS_FIRST       =       10h
  156. HIDE_FILE       =       40h
  157. CREAT_TEMP      =       80h
  158. NO_PREALLOC     =       100h
  159. CHECK_NET       =       200h
  160. DONT_SWAP_ENV   =       4000h
  161. ;
  162. ;       Return codes
  163. ;
  164. RC_TOOLOW       =       0102h
  165. RC_BADPREP      =       0500h
  166. RC_MCBERROR     =       0501h
  167. RC_SWAPERROR    =       0502h
  168. ;
  169. EMM_INT         =       67h
  170. ;
  171. ;       The EXEC function parameter block
  172. ;
  173. exec_block      struc
  174. envseg  dw      ?               ; environment segment
  175. ppar    dw      ?               ; program parameter string offset
  176. pparseg dw      ?               ; program parameter string segment
  177. fcb1    dw      ?               ; FCB offset
  178. fcb1seg dw      ?               ; FCB segment
  179. fcb2    dw      ?               ; FCB offset
  180. fcb2seg dw      ?               ; FCB segment
  181. exec_block      ends
  182. ;
  183. ;       Structure of an XMS move control block
  184. ;
  185. xms_control     struc
  186. lenlo           dw      ?       ; length to move (doubleword)
  187. lenhi           dw      ?
  188. srchnd          dw      ?       ; source handle (0 for standard memory)
  189. srclo           dw      ?       ; source address (doubleword or seg:off)
  190. srchi           dw      ?
  191. desthnd         dw      ?       ; destination handle (0 for standard memory)
  192. destlo          dw      ?       ; destination address (doubleword or seg:off)
  193. desthi          dw      ?
  194. xms_control     ends
  195. ;
  196. ;       The structure of the start of an MCB (memory control block)
  197. ;
  198. mcb             struc
  199. id              db      ?
  200. owner           dw      ?
  201. paras           dw      ?
  202. mcb             ends
  203. ;
  204. ;       The structure of an internal MCB descriptor.
  205. ;       CAUTION: This structure is assumed to be no larger than 16 bytes
  206. ;       in several places in the code, and to be exactly 16 bytes when
  207. ;       swapping in from file. Be careful when changing this structure.
  208. ;
  209. mcbdesc         struc
  210. addr            dw      ?       ; paragraph address of the MCB
  211. msize           dw      ?       ; size in paragraphs (excluding header)
  212. swoffset        dw      ?       ; swap offset (0 in all blocks except first)
  213. swsize          dw      ?       ; swap size (= msize + 1 except in first)
  214. num_follow      dw      ?       ; number of following MCBs
  215.                 dw      3 dup(?) ; pad to paragraph (16 bytes)
  216. mcbdesc         ends
  217. ;
  218. ;       The variable block set up by prep_swap
  219. ;
  220. prep_block      struc
  221. xmm             dd      ?               ; XMM entry address
  222. first_mcb       dw      ?               ; Segment of first MCB
  223. psp_mcb         dw      ?               ; Segment of MCB of our PSP
  224. env_mcb         dw      ?               ; MCB of Environment segment
  225. noswap_mcb      dw      ?               ; MCB that may not be swapped
  226. ems_pageframe   dw      ?               ; EMS page frame address
  227. handle          dw      ?               ; EMS/XMS/File handle
  228. total_mcbs      dw      ?               ; Total number of MCBs
  229. swapmethod      db      ?               ; Method for swapping
  230. swapfilename    db      81 dup(?)       ; Swap file name if swapping to file
  231. prep_block      ends
  232. ;
  233. ;----------------------------------------------------------------------
  234. ;
  235. ;       Since we'll be moving code and data around in memory,
  236. ;       we can't address locations in the resident block with
  237. ;       normal address expressions. MASM does not support
  238. ;       defining variables with a fixed offset, so we have to resort
  239. ;       to a kludge, and define the shrunk-down code as a structure.
  240. ;       It would also be possible to use an absolute segment for the
  241. ;       definition, but this is not supported by the Turbo Pascal linker.
  242. ;
  243. ;       All references to low-core variables from low-core itself
  244. ;       are made through DS, so we define a text macro "lmem" that
  245. ;       expands to "ds:". When setting up low core from the normal
  246. ;       code, ES is used to address low memory, so this can't be used.
  247. ;
  248. lmem    equ     <ds:>
  249. ;
  250. ;       The memory structure for the shrunk-down code, excluding the
  251. ;       code itself. The code follows this block.
  252. ;
  253. parseg          struc
  254.                 db      2ch dup(?)
  255. psp_envptr      dw      ?
  256.                 db      5ch-2eh dup(?)  ; start after PSP
  257. ;
  258. save_ss         dw      ?               ; 5C - saved global ss
  259. save_sp         dw      ?               ; 5E - saved global sp
  260. xfcb1           db      16 dup(?)       ; 60..6F - default FCB
  261. xfcb2           db      16 dup(?)       ; 70..7F - default FCB
  262. zero            dw      ?               ; 80 Zero command tail length (dummy)
  263. ;
  264. expar           db      TYPE exec_block dup (?) ; exec-parameter-block
  265. spx             dw      ?               ; saved local sp
  266. div0_off        dw      ?               ; divide by zero vector save
  267. div0_seg        dw      ?
  268. filename        db      82 dup(?)       ; exec filename
  269. progpars        db      128 dup(?)      ; command tail
  270.                 db      stacklen dup(?) ; local stack space
  271. mystack         db      ?
  272. lprep           db      TYPE prep_block dup(?)  ; the swapping variables
  273. lcurrdesc       db      TYPE mcbdesc dup(?)     ; the current MCB descriptor
  274. lxmsctl         db      TYPE xms_control dup(?)
  275. eretcode        dw      ?               ; EXEC return code
  276. retflags        dw      ?               ; EXEC return flags
  277. cgetmcb         dw      ?               ; address of get_mcb
  278. ;
  279. parseg  ends
  280. ;
  281. param_len       =       ((TYPE parseg + 1) / 2) * 2     ; make even
  282. codebeg         =       param_len
  283. ;
  284.         .code
  285. ;
  286. ;------------------------------------------------------------------------
  287. ;
  288. lowcode_begin:
  289. ;
  290. ;       The following parts of the program code will be moved to
  291. ;       low core and executed there, so there must be no absolute
  292. ;       memory references.
  293. ;       The call to get_mcb must be made indirect, since the offset
  294. ;       from the swap-in routine to get_mcb will not be the same
  295. ;       after moving.
  296. ;
  297. ;
  298. ;       get_mcb allocates a block of memory by modifying the MCB chain
  299. ;       directly.
  300. ;
  301. ;       On entry, lcurrdesc has the mcb descriptor for the block to
  302. ;                 allocate.
  303. ;
  304. ;       On exit,  Carry is set if the block couldn't be allocated.
  305. ;
  306. ;       Uses    AX, BX, CX, ES
  307. ;       Modifies lprep.first_mcb
  308. ;
  309. get_mcb proc    near
  310. ;
  311.         mov     ax,lmem lprep.first_mcb
  312.         mov     bx,lmem lcurrdesc.addr
  313. ;
  314. getmcb_loop:
  315.         mov     es,ax
  316.         cmp     ax,bx
  317.         ja      gmcb_abort              ; halt if MCB > wanted
  318.         je      mcb_found               ; jump if same addr as wanted
  319.         add     ax,es:paras             ; last addr
  320.         inc     ax                      ; next mcb
  321.         cmp     ax,bx
  322.         jbe     getmcb_loop             ; Loop if next <= wanted
  323. ;
  324. ;
  325. ;       The wanted MCB starts within the current MCB. We now have to
  326. ;       create a new MCB at the wanted position, which is initially
  327. ;       free, and shorten the current MCB to reflect the reduced size.
  328. ;
  329.         cmp     es:owner,0
  330.         jne     gmcb_abort              ; halt if not free
  331.         mov     bx,es                   ; current
  332.         inc     bx                      ; + 1 (header doesn't count)
  333.         mov     ax,lmem lcurrdesc.addr
  334.         sub     ax,bx                   ; paragraphs between MCB and wanted
  335.         mov     bx,es:paras             ; paras in current MCB
  336.         sub     bx,ax                   ; remaining paras
  337.         dec     bx                      ; -1 for header
  338.         mov     es:paras,ax             ; set new size for current
  339.         mov     cl,es:id                ; old id
  340.         mov     es:id,4dh               ; set id: there is a next
  341.         mov     ax,lmem lcurrdesc.addr
  342.         mov     es,ax
  343.         mov     es:id,cl                ; and init to free
  344.         mov     es:owner,0
  345.         mov     es:paras,bx
  346. ;
  347. ;       We have found an MCB at the right address. If it's not free,
  348. ;       abort. Else check the size. If the size is ok, we're done
  349. ;       (more or less).
  350. ;
  351. mcb_found:
  352.         mov     es,ax
  353.         cmp     es:owner,0
  354.         je      mcb_check               ; continue if free
  355. ;
  356. gmcb_abort:
  357.         stc
  358.         ret
  359. ;
  360. mcb_check:
  361.         mov     ax,es:paras             ; size
  362.         cmp     ax,lmem lcurrdesc.msize ; needed size
  363.         jae     mcb_ok                  ; ok if enough space
  364. ;
  365. ;       If there's not enough room in this MCB, check if the next
  366. ;       MCB is free, too. If so, coalesce both MCB's and check again.
  367. ;
  368.         cmp     es:id,4dh
  369.         jnz     gmcb_abort              ; halt if no next
  370.         push    es                      ; save current
  371.         mov     bx,es
  372.         add     ax,bx
  373.         inc     ax                      ; next MCB
  374.         mov     es,ax
  375.         cmp     es:owner,0              ; next free ?
  376.         jne     gmcb_abort              ; halt if not
  377.         mov     ax,es:paras             ; else load size
  378.         inc     ax                      ; + 1 for header
  379.         mov     cl,es:id                ; and load ID
  380.         pop     es                      ; back to last MCB
  381.         add     es:paras,ax             ; increase size
  382.         mov     es:id,cl                ; and store ID
  383.         jmp     mcb_check               ; now try again
  384. ;
  385. ;       The MCB is free and large enough. If it's larger than the
  386. ;       wanted size, create another MCB after the wanted.
  387. ;
  388. mcb_ok:
  389.         mov     bx,es:paras
  390.         sub     bx,lmem lcurrdesc.msize
  391.         jz      mcb_no_next             ; ok, no next to create
  392.         push    es
  393.         dec     bx                      ; size of next block
  394.         mov     ax,es
  395.         add     ax,lmem lcurrdesc.msize
  396.         inc     ax                      ; next MCB addr
  397.         mov     cl,es:id                ; id of this block
  398.         mov     es,ax                   ; address next
  399.         mov     es:id,cl                ; store id
  400.         mov     es:paras,bx             ; store size
  401.         mov     es:owner,0              ; and mark as free
  402.         pop     es                      ; back to old MCB
  403.         mov     es:id,4dh               ; mark next block present
  404.         mov     ax,lmem lcurrdesc.msize ; and set size to wanted
  405.         mov     es:paras,ax
  406. ;
  407. mcb_no_next:
  408.         mov     es:owner,cx             ; set owner to current PSP
  409. ;
  410. ;       Set the 'first_mcb' pointer to the current one, so we don't
  411. ;       walk through all the previous blocks the next time.
  412. ;       Also, check if the block we just allocated is the environment
  413. ;       segment of the program. If so, restore the environment pointer
  414. ;       in the PSP.
  415. ;
  416.         mov     ax,es
  417.         mov     lmem lprep.first_mcb,ax
  418.         cmp     lmem lprep.env_mcb,ax
  419.         jne     getmcb_finis
  420.         inc     ax
  421.         mov     lmem psp_envptr,ax
  422. ;
  423. getmcb_finis:
  424.         clc
  425.         ret                             ; all finished (whew!)
  426. ;
  427. get_mcb endp
  428. ;
  429. ;
  430. ireti:
  431.         iret
  432. ;
  433. ;
  434. ;       The actual EXEC call.
  435. ;       Registers on entry:
  436. ;               BX      = paragraphs to keep (0 if no swap)
  437. ;               CX      = length of environment to copy (words) or zero
  438. ;               DS:SI   = environment source
  439. ;               ES:DI   = environment destination
  440. ;               (ES = our low core code segment)
  441. ;
  442. ;
  443. ;       copy environment buffer down if present
  444. ;
  445. doexec:
  446.         jcxz    noenvcpy
  447.         rep movsw
  448. ;
  449. noenvcpy:
  450.         push    es                      ; DS = ES = low core = PSP
  451.         pop     ds
  452.         or      bx,bx
  453.         jz      no_shrink
  454. ;
  455. ;       first, shrink the base memory block down.
  456. ;
  457.         mov     ah,04ah
  458.         int     21h                     ; resize memory block
  459. ;
  460. ;       Again walk all MCBs. This time, all blocks owned by the
  461. ;       current process are released.
  462. ;
  463.         mov     si,lmem lprep.first_mcb
  464.         or      si,si
  465.         jz      no_shrink
  466.         mov     dx,lmem lprep.psp_mcb
  467.         mov     bx,dx
  468.         inc     bx                      ; base PSP (MCB owner)
  469.         mov     di,lmem lprep.noswap_mcb
  470. ;
  471. free_loop:
  472.         cmp     si,dx
  473.         je      free_next               ; don't free base block
  474.         cmp     si,di
  475.         je      free_next
  476.         mov     es,si
  477.         cmp     bx,es:owner             ; our process?
  478.         jne     free_next               ; next if not
  479.         cmp     si,lmem lprep.env_mcb   ; is this the environment block?
  480.         jne     free_noenv
  481.         mov     ds:psp_envptr,0         ; else clear PSP pointer
  482. ;
  483. free_noenv:
  484.         inc     si
  485.         mov     es,si
  486.         dec     si
  487.         mov     ah,049h                 ; free memory block
  488.         int     21h
  489. ;
  490. free_next:
  491.         mov     es,si
  492.         cmp     es:id,4dh               ; normal block?
  493.         jne     free_ready              ; ready if end of chain
  494.         add     si,es:paras             ; start + length
  495.         inc     si                      ; next MCB
  496.         jmp     free_loop
  497. ;
  498. free_ready:
  499.         mov     ax,ds
  500.         mov     es,ax
  501. ;
  502. no_shrink:
  503.         mov     dx,filename             ; params for exec
  504.         mov     bx,expar
  505.         mov     ax,04b00h
  506.         int     21h                     ; exec
  507. ;
  508. ;       Return from EXEC system call. Don't count on any register except
  509. ;       CS to be restored (DOS 2.11 and previous versions killed all regs).
  510. ;
  511.         mov     bx,cs
  512.         mov     ds,bx
  513.         mov     es,bx
  514.         mov     ss,bx
  515.         mov     sp,lmem spx
  516.         cld
  517.         mov     lmem eretcode,ax        ; save return code
  518.         pushf
  519.         pop     bx
  520.         mov     lmem retflags,bx        ; and returned flags
  521. ;
  522.         cmp     lmem lprep.swapmethod,0
  523.         je      exec_memok
  524.         jg      exec_expand
  525. ;
  526. ;       Terminate.
  527. ;
  528.         test    bx,1                    ; carry?
  529.         jnz     exec_term               ; use EXEc retcode if set
  530.         mov     ah,4dh                  ; else get program return code
  531.         int     21h
  532. ;
  533. exec_term:
  534.         mov     ah,4ch
  535.         int     21h
  536. ;
  537. ;
  538. exec_expand:
  539.         mov     ah,4ah                  ; expand memory
  540.         mov     bx,lmem lcurrdesc.msize
  541.         int     21h
  542.         jnc     exec_memok
  543.         mov     ax,4cffh
  544.         int     21h                     ; terminate on error
  545. ;
  546. ;       Swap memory back
  547. ;
  548.         nop
  549. ;
  550. exec_memok:
  551. ;
  552. ;       FALL THROUGH to the appropriate swap-in routine
  553. ;
  554. ;
  555. getmcboff       =       offset get_mcb - offset lowcode_begin
  556. iretoff         =       offset ireti - offset lowcode_begin
  557. doexec_entry    =       offset doexec - offset lowcode_begin
  558. base_length     =       offset $ - offset lowcode_begin
  559. ;
  560. ;-----------------------------------------------------------------------
  561. ;
  562. ;       The various swap in routines follow. Only one of the routines
  563. ;       is copied to low memory.
  564. ;       Note that the routines are never actually called, the EXEC return
  565. ;       code falls through. The final RET thus will return to the restored
  566. ;       memory image.
  567. ;
  568. ;       On entry, DS must point to low core.
  569. ;       On exit to the restored code, DS is unchanged.
  570. ;
  571. ;
  572. ;       swapin_ems:     swap in from EMS.
  573. ;
  574. swapin_ems      proc    far
  575. ;
  576.         xor     bx,bx
  577.         mov     si,ems_parasize
  578.         mov     dx,lmem lprep.handle    ; EMS handle
  579. ;
  580. swinems_main:
  581.         push    ds
  582.         mov     cx,lmem lcurrdesc.swsize        ; block length in paras
  583.         mov     di,lmem lcurrdesc.swoffset      ; swap offset
  584.         mov     es,lmem lcurrdesc.addr          ; segment to swap
  585.         mov     ds,lmem lprep.ems_pageframe     ; page frame address
  586. ;
  587.         mov     ax,ems_parasize         ; max length
  588.         sub     ax,si                   ; minus current offset
  589.         jnz     swinems_ok              ; go copy if nonzero
  590. ;
  591. swinems_loop:
  592.         mov     ax,4400h                ; map in next page
  593.         int     EMM_INT
  594.         or      ah,ah
  595.         jnz     swinems_error
  596.         mov     si,0                    ; reset offset
  597.         inc     bx                      ; bump up page number
  598.         mov     ax,ems_parasize         ; max length to copy
  599. ;
  600. swinems_ok:
  601.         cmp     ax,cx                   ; length to copy
  602.         jbe     swinems_doit            ; go do it if <= total length
  603.         mov     ax,cx                   ; else use total length
  604. ;
  605. swinems_doit:
  606.         sub     cx,ax                   ; subtract copy length from total
  607.         push    cx                      ; and save
  608.         push    ax                      ; save the copy length in paras
  609.         push    si
  610.         push    di
  611.         mov     cl,3
  612.         shl     ax,cl                   ; convert to number of words (!)
  613.         inc     cl
  614.         shl     si,cl                   ; convert to byte address
  615.         mov     cx,ax
  616.         rep movsw
  617.         pop     di
  618.         pop     si
  619.         pop     cx                      ; copy length in paras
  620.         mov     ax,es
  621.         add     ax,cx                   ; add copy length to dest segment
  622.         add     si,cx                   ; and EMS page offset
  623.         mov     es,ax
  624.         pop     cx                      ; remaining length
  625.         or      cx,cx                   ; did we copy everything?
  626.         jnz     swinems_loop            ; go loop if not
  627. ;
  628.         pop     ds
  629.         cmp     lmem lcurrdesc.num_follow,0     ; another MCB?
  630.         je      swinems_complete        ; exit if not
  631. ;
  632. ;       Another MCB follows, read next mcb descriptor into currdesc
  633. ;
  634.         cmp     si,ems_parasize
  635.         jb      swinems_nonewpage       ; no new block needed
  636.         mov     ax,4400h                ; map page, phys = 0
  637.         int     EMM_INT
  638.         or      ah,ah
  639.         jnz     swinems_error1
  640.         mov     si,0
  641.         inc     bx
  642. ;
  643. swinems_nonewpage:
  644.         push    si
  645.         push    ds
  646.         mov     ax,ds
  647.         mov     es,ax
  648.         mov     ds,lmem lprep.ems_pageframe     ; page frame address
  649.         mov     cl,4
  650.         shl     si,cl                   ; convert to byte address
  651.         mov     cx,TYPE mcbdesc
  652.         mov     di,lcurrdesc
  653.         rep movsb
  654.         pop     ds
  655.         pop     si
  656.         inc     si                      ; one paragraph
  657. ;
  658.         push    bx
  659.         call    lmem cgetmcb
  660.         pop     bx
  661.         jc      swinems_error1
  662.         jmp     swinems_main
  663. ;
  664. swinems_complete:
  665.         mov     ah,45h                  ; release EMS pages
  666.         int     EMM_INT
  667.         ret
  668. ;
  669. swinems_error:
  670.         pop     ds
  671. swinems_error1:
  672.         mov     ah,45h                  ; release EMS pages on error
  673.         int     EMM_INT
  674.         mov     ax,4cffh
  675.         int     21h                     ; terminate
  676. ;
  677. swapin_ems      endp
  678. ;
  679. swinems_length  = offset $ - offset swapin_ems
  680. ;
  681. ;
  682. ;       swapin_xms:     swap in from XMS.
  683. ;
  684. swapin_xms      proc    far
  685. ;
  686.         mov     ax,lmem lprep.handle    ; XMS handle
  687.         mov     lmem lxmsctl.srchnd,ax  ; source is XMS
  688.         mov     lmem lxmsctl.desthnd,0  ; dest is normal memory
  689.         mov     lmem lxmsctl.srclo,0
  690.         mov     lmem lxmsctl.srchi,0
  691. ;
  692. swinxms_main:
  693.         mov     ax,lmem lcurrdesc.swsize ; size in paragraphs
  694.         mov     cl,4
  695.         rol     ax,cl                   ; size in bytes + high nibble
  696.         mov     dx,ax
  697.         and     ax,0fff0h               ; low word
  698.         and     dx,0000fh               ; high word
  699.         mov     lmem lxmsctl.lenlo,ax   ; into control block
  700.         mov     lmem lxmsctl.lenhi,dx
  701.         mov     ax,lmem lcurrdesc.swoffset      ; swap offset
  702.         mov     lmem lxmsctl.destlo,ax          ; into control block
  703.         mov     ax,lmem lcurrdesc.addr          ; segment to swap
  704.         mov     lmem lxmsctl.desthi,ax
  705.         mov     si,lxmsctl
  706.         mov     ah,0bh
  707.         call    lmem lprep.xmm          ; move it
  708.         or      ax,ax
  709.         jz      swinxms_error
  710.         mov     ax,lmem lxmsctl.lenlo   ; adjust source addr
  711.         add     lmem lxmsctl.srclo,ax
  712.         mov     ax,lmem lxmsctl.lenhi
  713.         adc     lmem lxmsctl.srchi,ax
  714. ;
  715.         cmp     lmem lcurrdesc.num_follow,0     ; another MCB?
  716.         je      swinxms_complete
  717. ;
  718.         mov     lmem lxmsctl.lenlo,TYPE mcbdesc
  719.         mov     lmem lxmsctl.lenhi,0
  720.         mov     lmem lxmsctl.desthi,ds
  721.         mov     lmem lxmsctl.destlo,lcurrdesc
  722.         mov     si,lxmsctl
  723.         mov     ah,0bh
  724.         call    lmem lprep.xmm          ; move it
  725.         or      ax,ax
  726.         jz      swinxms_error
  727.         add     lmem lxmsctl.srclo,16   ; one paragraph
  728.         adc     lmem lxmsctl.srchi,0
  729. ;
  730.         call    lmem cgetmcb
  731.         jc      swinxms_error
  732.         jmp     swinxms_main
  733. ;
  734. swinxms_complete:
  735.         mov     ah,0ah                  ; release XMS frame
  736.         mov     dx,lmem lprep.handle    ; XMS handle
  737.         call    lmem lprep.xmm
  738.         ret
  739. ;
  740. swinxms_error:
  741.         mov     ah,0ah                  ; release XMS frame on error
  742.         call    lmem lprep.xmm
  743.         mov     ax,4c00h
  744.         int     21h
  745. ;
  746. swapin_xms      endp
  747. ;
  748. swinxms_length  = offset $ - offset swapin_xms
  749. ;
  750. ;
  751. ;       swapin_file:    swap in from file.
  752. ;
  753. swapin_file     proc    far
  754. ;
  755.         mov     dx,lprep.swapfilename
  756.         mov     ax,3d00h                        ; open file
  757.         int     21h
  758.         jc      swinfile_error2
  759.         mov     bx,ax                           ; file handle
  760. ;
  761. swinfile_main:
  762.         push    ds
  763.         mov     cx,lmem lcurrdesc.swsize        ; size in paragraphs
  764.         mov     dx,lmem lcurrdesc.swoffset      ; swap offset
  765.         mov     ds,lmem lcurrdesc.addr          ; segment to swap
  766. ;
  767. swinfile_loop:
  768.         mov     ax,cx
  769.         cmp     ah,8h                   ; above 32k?
  770.         jbe     swinfile_ok             ; go read if not
  771.         mov     ax,800h                 ; else read 32k
  772. ;
  773. swinfile_ok:
  774.         sub     cx,ax                   ; remaining length
  775.         push    cx                      ; save it
  776.         push    ax                      ; and save paras to read
  777.         mov     cl,4
  778.         shl     ax,cl                   ; convert to bytes
  779.         mov     cx,ax
  780.         mov     ah,3fh                  ; read
  781.         int     21h
  782.         jc      swinfile_error
  783.         cmp     ax,cx
  784.         jne     swinfile_error
  785.         pop     cx                      ; paras read
  786.         mov     ax,ds
  787.         add     ax,cx                   ; bump up dest segment
  788.         mov     ds,ax
  789.         pop     cx                      ; remaining length
  790.         or      cx,cx                   ; anything left?
  791.         jnz     swinfile_loop           ; go loop if yes
  792. ;
  793.         pop     ds
  794.         cmp     lmem lcurrdesc.num_follow,0     ; another MCB?
  795.         je      swinfile_complete       ; ready if not
  796.         mov     cx,16                   ; read one paragraph
  797.         mov     dx,lcurrdesc
  798.         mov     ah,3fh
  799.         int     21h
  800.         jc      swinfile_error1
  801.         cmp     ax,cx
  802.         jne     swinfile_error1
  803. ;
  804.         push    bx
  805.         call    lmem cgetmcb
  806.         pop     bx
  807.         jc      swinfile_error1
  808.         jmp     swinfile_main
  809. ;
  810. ;
  811. swinfile_complete:
  812.         mov     ah,3eh                  ; close file
  813.         int     21h
  814.         mov     dx,lprep.swapfilename
  815.         mov     ah,41h                  ; delete file
  816.         int     21h
  817.         ret
  818. ;
  819. swinfile_error:
  820.         pop     cx
  821.         pop     cx
  822.         pop     ds
  823. swinfile_error1:
  824.         mov     ah,3eh                  ; close file
  825.         int     21h
  826. swinfile_error2:
  827.         mov     dx,lprep.swapfilename
  828.         mov     ah,41h                  ; delete file
  829.         int     21h
  830.         mov     ax,4cffh
  831.         int     21h
  832. ;
  833. swapin_file     endp
  834. ;
  835. swinfile_length = offset $ - offset swapin_file
  836. ;
  837. ;
  838. ;       swapin_none:    no swap, return immediately.
  839. ;
  840. swapin_none     proc    far
  841. ;
  842.         ret
  843. ;
  844. swapin_none     endp
  845. ;
  846. ;
  847.         IF      swinems_length GT swinxms_length
  848. swcodelen       =       swinems_length
  849.         ELSE
  850. swcodelen       =       swinxms_length
  851.         ENDIF
  852.         IF      swinfile_length GT swcodelen
  853. swcodelen       =       swinfile_length
  854.         ENDIF
  855. ;
  856. swap_codelen    =       ((swcodelen + 1) / 2) * 2
  857. ;
  858. codelen         =       base_length + swap_codelen
  859. reslen          =       codebeg + codelen
  860. keep_paras      =       (reslen + 15) shr 4     ; paragraphs to keep
  861. swapbeg         =       keep_paras shl 4        ; start of swap space
  862. savespace       =       swapbeg - 5ch   ; length of overwritten area
  863. ;
  864. ;--------------------------------------------------------------------
  865. ;
  866.         IFDEF   PASCAL
  867.         .data
  868.         ELSE
  869.         IFDEF   TC_HUGE
  870.         .fardata?       my_data
  871.         ELSE
  872.         .data?
  873.         ENDIF
  874.         ENDIF
  875. ;
  876. ;
  877. ;       Space for saving the part of the memory image below the
  878. ;       swap area that is overwritten by our code.
  879. ;
  880. save_dat        db      savespace dup(?)
  881. ;
  882. ;       Variables used while swapping out.
  883. ;       The "prep" structure is initialized by prep_swap.
  884. ;
  885. prep            prep_block      <>
  886. nextmcb         mcbdesc         <>
  887. currdesc        mcbdesc         <>
  888. xmsctl          xms_control     <>
  889. ems_curpage     dw              ?       ; current EMS page number
  890. ems_curoff      dw              ?       ; current EMS offset (paragraph)
  891. ;
  892. ;--------------------------------------------------------------------
  893. ;
  894.         .code
  895. ;
  896. ;       swapout_ems:    swap out an MCB block to EMS.
  897. ;
  898. ;       Entry:  "currdesc"      contains description of block to swap
  899. ;               "nextmcb"       contains MCB-descriptor of next block
  900. ;                               if currdesc.num_follow is nonzero
  901. ;
  902. ;       Exit:   0 if OK, != 0 if error, Zero-flag set accordingly.
  903. ;
  904. ;       Uses:   All regs excpt DS
  905. ;
  906. swapout_ems     proc    near
  907. ;
  908.         push    ds
  909.         mov     cx,currdesc.swsize      ; block length in paras
  910.         mov     si,currdesc.swoffset    ; swap offset
  911.         mov     dx,prep.handle          ; EMS handle
  912.         mov     bx,ems_curpage          ; current EMS page
  913.         mov     di,ems_curoff           ; current EMS page offset (paras)
  914.         mov     es,prep.ems_pageframe   ; page frame address
  915.         mov     ds,currdesc.addr        ; segment to swap
  916. ;
  917.         mov     ax,ems_parasize         ; max length
  918.         sub     ax,di                   ; minus current offset
  919.         jnz     swems_ok                ; go copy if there's room
  920. ;
  921. swems_loop:
  922.         mov     ax,4400h                ; map in next page
  923.         int     EMM_INT
  924.         or      ah,ah
  925.         jnz     swems_error
  926.         mov     di,0                    ; reset offset
  927.         inc     bx                      ; bump up page number
  928.         mov     ax,ems_parasize         ; max length to copy
  929. ;
  930. swems_ok:
  931.         cmp     ax,cx                   ; length to copy
  932.         jbe     swems_doit              ; go do it if <= total length
  933.         mov     ax,cx                   ; else use total length
  934. ;
  935. swems_doit:
  936.         sub     cx,ax                   ; subtract copy length from total
  937.         push    cx                      ; and save
  938.         push    ax                      ; save the copy length in paras
  939.         push    si
  940.         push    di
  941.         mov     cl,3
  942.         shl     ax,cl                   ; convert to number of words (!)
  943.         inc     cl
  944.         shl     di,cl                   ; convert to byte address
  945.         mov     cx,ax
  946.         rep movsw
  947.         pop     di
  948.         pop     si
  949.         pop     cx                      ; copy length in paras
  950.         mov     ax,ds
  951.         add     ax,cx                   ; add copy length to source segment
  952.         add     di,cx                   ; and EMS page offset
  953.         mov     ds,ax
  954.         pop     cx                      ; remaining length
  955.         or      cx,cx                   ; did we copy everything?
  956.         jnz     swems_loop              ; go loop if not
  957. ;
  958.         pop     ds
  959.         cmp     currdesc.num_follow,0   ; another MCB?
  960.         je      swems_complete          ; exit if not
  961. ;
  962. ;       Another MCB follows, append nextmcb to save block.
  963. ;
  964.         cmp     di,ems_parasize
  965.         jb      swems_nonewpage         ; no new block needed
  966.         mov     ax,4400h                ; map page, phys = 0
  967.         int     EMM_INT
  968.         or      ah,ah
  969.         jnz     swems_error1
  970.         mov     di,0
  971.         inc     bx
  972. ;
  973. swems_nonewpage:
  974.         push    di
  975.         mov     cl,4
  976.         shl     di,cl                   ; convert to byte address
  977.         mov     cx,TYPE mcbdesc
  978.         mov     si,offset nextmcb
  979.         rep movsb
  980.         pop     di
  981.         inc     di                      ; one paragraph
  982. ;
  983. swems_complete:
  984.         mov     ems_curpage,bx
  985.         mov     ems_curoff,di
  986.         xor     ax,ax
  987.         ret
  988. ;
  989. swems_error:
  990.         pop     ds
  991. swems_error1:
  992.         mov     ah,45h                  ; release EMS pages on error
  993.         int     EMM_INT
  994.         mov     ax,RC_SWAPERROR
  995.         or      ax,ax
  996.         ret
  997. ;
  998. swapout_ems     endp
  999. ;
  1000. ;
  1001. ;       swapout_xms:    swap out an MCB block to XMS.
  1002. ;
  1003. ;       Entry:  "currdesc"      contains description of block to swap
  1004. ;               "nextmcb"       contains MCB-descriptor of next block
  1005. ;                               if currdesc.num_follow is nonzero
  1006. ;
  1007. ;       Exit:   0 if OK, -1 if error, Zero-flag set accordingly.
  1008. ;
  1009. ;       Uses:   All regs excpt DS
  1010. ;
  1011. swapout_xms     proc    near
  1012. ;
  1013.         mov     ax,currdesc.swsize      ; size in paragraphs
  1014.         mov     cl,4
  1015.         rol     ax,cl                   ; size in bytes + high nibble
  1016.         mov     dx,ax
  1017.         and     ax,0fff0h               ; low word
  1018.         and     dx,0000fh               ; high word
  1019.         mov     xmsctl.lenlo,ax         ; into control block
  1020.         mov     xmsctl.lenhi,dx
  1021.         mov     xmsctl.srchnd,0         ; source is normal memory
  1022.         mov     ax,currdesc.swoffset    ; swap offset
  1023.         mov     xmsctl.srclo,ax         ; into control block
  1024.         mov     ax,currdesc.addr        ; segment to swap
  1025.         mov     xmsctl.srchi,ax
  1026.         mov     ax,prep.handle          ; XMS handle
  1027.         mov     xmsctl.desthnd,ax
  1028.         mov     si,offset xmsctl
  1029.         mov     ah,0bh
  1030.         call    prep.xmm                ; move it
  1031.         or      ax,ax
  1032.         jz      swxms_error
  1033.         mov     ax,xmsctl.lenlo         ; adjust destination addr
  1034.         add     xmsctl.destlo,ax
  1035.         mov     ax,xmsctl.lenhi
  1036.         adc     xmsctl.desthi,ax
  1037. ;
  1038.         cmp     currdesc.num_follow,0   ; another MCB?
  1039.         je      swxms_complete
  1040. ;
  1041.         mov     xmsctl.lenlo,TYPE mcbdesc
  1042.         mov     xmsctl.lenhi,0
  1043.         mov     xmsctl.srchi,ds
  1044.         mov     xmsctl.srclo,offset nextmcb
  1045.         mov     si,offset xmsctl
  1046.         mov     ah,0bh
  1047.         call    prep.xmm                ; move it
  1048.         or      ax,ax
  1049.         jz      swxms_error
  1050.         add     xmsctl.destlo,16        ; one paragraph
  1051.         adc     xmsctl.desthi,0
  1052. ;
  1053. swxms_complete:
  1054.         xor     ax,ax
  1055.         ret
  1056. ;
  1057. swxms_error:
  1058.         mov     ah,0ah                  ; release XMS frame on error
  1059.         mov     dx,prep.handle          ; XMS handle
  1060.         call    prep.xmm
  1061.         mov     ax,RC_SWAPERROR
  1062.         or      ax,ax
  1063.         ret
  1064. ;
  1065. swapout_xms     endp
  1066. ;
  1067. ;
  1068. ;       swapout_file:   swap out an MCB block to file.
  1069. ;
  1070. ;       Entry:  "currdesc"      contains description of block to swap
  1071. ;               "nextmcb"       contains MCB-descriptor of next block
  1072. ;                               if currdesc.num_follow is nonzero
  1073. ;
  1074. ;       Exit:   0 if OK, -1 if error, Zero-flag set accordingly.
  1075. ;
  1076. ;       Uses:   All regs excpt DS
  1077. ;
  1078. swapout_file    proc    near
  1079. ;
  1080.         push    ds
  1081.         mov     cx,currdesc.swsize      ; size in paragraphs
  1082.         mov     bx,prep.handle          ; file handle
  1083.         mov     dx,currdesc.swoffset    ; swap offset
  1084.         mov     ds,currdesc.addr        ; segment to swap
  1085. ;
  1086. swfile_loop:
  1087.         mov     ax,cx
  1088.         cmp     ah,8h                   ; above 32k?
  1089.         jbe     swfile_ok               ; go write if not
  1090.         mov     ax,800h                 ; else write 32k
  1091. ;
  1092. swfile_ok:
  1093.         sub     cx,ax                   ; remaining length
  1094.         push    cx                      ; save it
  1095.         push    ax                      ; and save paras to write
  1096.         mov     cl,4
  1097.         shl     ax,cl                   ; convert to bytes
  1098.         mov     cx,ax
  1099.         mov     ah,40h                  ; write
  1100.         int     21h
  1101.         jc      swfile_error
  1102.         cmp     ax,cx
  1103.         jne     swfile_error
  1104.         pop     cx                      ; paras written
  1105.         mov     ax,ds
  1106.         add     ax,cx                   ; bump up source segment
  1107.         mov     ds,ax
  1108.         pop     cx                      ; remaining length
  1109.         or      cx,cx                   ; anything left?
  1110.         jnz     swfile_loop             ; go loop if yes
  1111. ;
  1112.         pop     ds
  1113.         cmp     currdesc.num_follow,0   ; another MCB?
  1114.         je      swfile_complete         ; ready if not
  1115.         mov     cx,16                   ; write one paragraph
  1116.         mov     dx,offset nextmcb
  1117.         mov     ah,40h
  1118.         int     21h
  1119.         jc      swfile_error1
  1120.         cmp     ax,cx
  1121.         jne     swfile_error1
  1122. ;
  1123. swfile_complete:
  1124.         xor     ax,ax
  1125.         ret
  1126. ;
  1127. swfile_error:
  1128.         pop     cx
  1129.         pop     cx
  1130.         pop     ds
  1131. swfile_error1:
  1132.         mov     ah,3eh                  ; close file
  1133.         int     21h
  1134.         mov     dx,offset prep.swapfilename
  1135.         mov     ah,41h                  ; delete file
  1136.         int     21h
  1137.         mov     ax,RC_SWAPERROR
  1138.         or      ax,ax
  1139.         ret
  1140. ;
  1141. swapout_file    endp
  1142. ;
  1143. ;--------------------------------------------------------------------------
  1144. ;--------------------------------------------------------------------------
  1145. ;
  1146. ;
  1147.         IFDEF   PASCAL
  1148.         IFDEF   FARCALL
  1149. do_spawn        PROC    far swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
  1150.         ELSE
  1151. do_spawn        PROC    near swapping: word, execfname: dword, params: dword, envlen: word, envp: dword
  1152.         ENDIF
  1153.         ELSE
  1154. do_spawn        PROC    uses si di,swapping: word, execfname:ptr byte,params:ptr byte,envlen:word,envp:ptr byte
  1155.         ENDIF
  1156.         local   datseg,pspseg,currmcb
  1157. ;
  1158.         IFDEF   TC_HUGE
  1159.         mov     ax,SEG my_data
  1160.         mov     ds,ax
  1161.         ENDIF
  1162. ;
  1163.         mov     datseg,ds               ; save default DS
  1164. ;
  1165.         IFDEF   PASCAL
  1166.         cld
  1167.         mov     bx,prefixseg
  1168.         ELSE
  1169.         IFDEF   TC_HUGE
  1170.         mov     ax,SEG _psp
  1171.         mov     es,ax
  1172.         mov     bx,es:_psp
  1173.         ELSE
  1174.         mov     bx,_psp
  1175.         ENDIF
  1176.         ENDIF
  1177.         mov     pspseg,bx
  1178. ;
  1179. ;
  1180. ;       Check if spawn is too low in memory
  1181. ;
  1182.         mov     ax,cs
  1183.         mov     dx,offset lowcode_begin
  1184.         mov     cl,4
  1185.         shr     dx,cl
  1186.         add     ax,dx                   ; normalized start of this code
  1187.         mov     dx,keep_paras           ; the end of the modified area
  1188.         add     dx,bx                   ; plus PSP = end paragraph
  1189.         cmp     ax,dx
  1190.         ja      doswap_ok       ; ok if start of code > end of low mem
  1191.         mov     ax,RC_TOOLOW
  1192.         ret
  1193. ;
  1194. doswap_ok:
  1195.         cmp     swapping,0
  1196.         jle     method_ok
  1197. ;
  1198. ;       check the swap method, to make sure prep_swap has been called
  1199. ;
  1200.         mov     al,prep.swapmethod
  1201.         cmp     al,USE_EMS
  1202.         je      method_ok
  1203.         cmp     al,USE_XMS
  1204.         je      method_ok
  1205.         cmp     al,USE_FILE
  1206.         je      method_ok
  1207.         mov     ax,RC_BADPREP
  1208.         ret
  1209. ;
  1210. ;       Save the memory below the swap space.
  1211. ;       We must do this before swapping, so the saved memory is
  1212. ;       in the swapped out image.
  1213. ;       Anything else we'd want to save on the stack or anywhere
  1214. ;       else in "normal" memory also has to be saved here, any
  1215. ;       modifications done to memory after the swap will be lost.
  1216. ;
  1217. ;       Note that the memory save is done even when not swapping,
  1218. ;       because we use some of the variables in low core for
  1219. ;       simplicity.
  1220. ;
  1221. method_ok:
  1222.         push    ds
  1223.         pop     es
  1224.         push    ds
  1225.         mov     ds,pspseg               ; DS points to PSP
  1226.         mov     si,5ch
  1227.         mov     di,offset save_dat
  1228.         mov     cx,savespace / 2        ; NOTE: savespace is always even
  1229.         rep movsw
  1230.         pop     ds
  1231. ;
  1232.         mov     ax,swapping
  1233.         cmp     ax,0
  1234.         jg      begin_swap
  1235. ;
  1236. ;       not swapping, prep_swap wasn't called. Init those variables in
  1237. ;       the 'prep' block we need in any case.
  1238. ;
  1239.         mov     prep.swapmethod,al
  1240.         je      no_reduce
  1241. ;
  1242.         mov     ax,pspseg
  1243.         dec     ax
  1244.         mov     prep.psp_mcb,ax
  1245.         mov     prep.first_mcb,ax
  1246.         inc     ax
  1247.         mov     es,ax
  1248.         mov     bx,es:psp_envptr
  1249.         mov     prep.env_mcb,bx
  1250.         mov     prep.noswap_mcb,0
  1251.         cmp     envlen,0
  1252.         jne     swp_can_swap_env
  1253.         mov     prep.noswap_mcb,bx
  1254. ;
  1255. swp_can_swap_env:
  1256.         xor     bx,bx
  1257.         mov     es,bx
  1258.         mov     ah,52h                  ; get list of lists
  1259.         int     21h
  1260.         mov     ax,es
  1261.         or      ax,bx
  1262.         jz      no_reduce
  1263.         mov     es,es:[bx-2]            ; first MCB
  1264.         cmp     es:id,4dh               ; normal ID?
  1265.         jne     no_reduce
  1266.         mov     prep.first_mcb,es
  1267. ;
  1268. no_reduce:
  1269.         jmp     no_swap1
  1270. ;
  1271. ;       set up first block descriptor
  1272. ;
  1273. begin_swap:
  1274.         mov     ax,prep.first_mcb
  1275.         mov     currmcb,ax
  1276.         mov     es,prep.psp_mcb         ; let ES point to base MCB
  1277.         mov     ax,es:paras
  1278.         mov     currdesc.msize,ax
  1279.         sub     ax,keep_paras
  1280.         mov     currdesc.swsize,ax
  1281.         mov     currdesc.addr,es
  1282.         mov     currdesc.swoffset,swapbeg + 16
  1283. ;               NOTE: swapbeg is 1 para higher when seen from MCB
  1284.         mov     ax,prep.total_mcbs
  1285.         mov     currdesc.num_follow,ax
  1286. ;
  1287. ;       init other vars
  1288. ;
  1289.         mov     xmsctl.destlo,0
  1290.         mov     xmsctl.desthi,0
  1291.         mov     ems_curpage,0
  1292.         mov     ems_curoff,ems_parasize
  1293. ;
  1294. ;       Do the swapping. Each MCB block (except the last) has an
  1295. ;       "mcbdesc" structure appended that gives location and size
  1296. ;       of the next MCB.
  1297. ;
  1298. swapout_main:
  1299.         cmp     currdesc.num_follow,0   ; next block?
  1300.         je      swapout_no_next         ; ok if not
  1301. ;
  1302. ;       There is another MCB block to be saved. So we don't have
  1303. ;       to do two calls to the save routine with complicated
  1304. ;       parameters, we set up the next MCB descriptor beforehand.
  1305. ;       Walk the MCB chain starting at the current MCB to find
  1306. ;       the next one belonging to this process.
  1307. ;
  1308.         mov     ax,currmcb
  1309.         mov     bx,pspseg
  1310.         mov     cx,prep.psp_mcb
  1311.         mov     dx,prep.noswap_mcb
  1312. ;
  1313. swm_mcb_walk:
  1314.         mov     es,ax
  1315.         cmp     ax,cx
  1316.         je      swm_next_mcb
  1317.         cmp     ax,dx
  1318.         je      swm_next_mcb
  1319. ;
  1320.         cmp     bx,es:owner             ; our process?
  1321.         je      swm_mcb_found           ; found it if yes
  1322. ;
  1323. swm_next_mcb:
  1324.         cmp     es:id,4dh               ; normal block?
  1325.         jne     swm_mcb_error           ; error if end of chain
  1326.         add     ax,es:paras             ; start + length
  1327.         inc     ax                      ; next MCB
  1328.         jmp     swm_mcb_walk
  1329. ;
  1330. ;       MCB found, set up an mcbdesc in the "nextmcb" structure
  1331. ;
  1332. swm_mcb_found:
  1333.         mov     nextmcb.addr,es
  1334.         mov     ax,es:paras             ; get number of paragraphs
  1335.         mov     nextmcb.msize,ax        ; and save
  1336.         inc     ax
  1337.         mov     nextmcb.swsize,ax
  1338.         mov     bx,es
  1339.         add     bx,ax
  1340.         mov     currmcb,bx
  1341.         mov     nextmcb.swoffset,0
  1342.         mov     ax,currdesc.num_follow
  1343.         dec     ax
  1344.         mov     nextmcb.num_follow,ax
  1345. ;
  1346. swapout_no_next:
  1347.         cmp     prep.swapmethod,USE_EMS
  1348.         je      swm_ems
  1349.         cmp     prep.swapmethod,USE_XMS
  1350.         je      swm_xms
  1351.         call    swapout_file
  1352.         jmp     short swm_next
  1353. ;
  1354. swm_ems:
  1355.         call    swapout_ems
  1356.         jmp     short swm_next
  1357. ;
  1358. swm_xms:
  1359.         call    swapout_xms
  1360. ;
  1361. swm_next:
  1362.         jnz     swapout_error
  1363.         cmp     currdesc.num_follow,0
  1364.         je      swapout_complete
  1365. ;
  1366. ;       next MCB exists, copy the "nextmcb" descriptor into
  1367. ;       currdesc, and loop.
  1368. ;
  1369.         mov     es,datseg
  1370.         mov     si,offset nextmcb
  1371.         mov     di,offset currdesc
  1372.         mov     cx,TYPE mcbdesc
  1373.         rep movsb
  1374.         jmp     swapout_main
  1375. ;
  1376. ;
  1377. swm_mcb_error:
  1378.         cmp     prep.swapmethod,USE_FILE
  1379.         je      swm_mcberr_file
  1380.         cmp     prep.swapmethod,USE_EMS
  1381.         je      swm_mcberr_ems
  1382. ;
  1383.         mov     ah,0ah                  ; release XMS frame on error
  1384.         mov     dx,prep.handle          ; XMS handle
  1385.         call    prep.xmm
  1386.         mov     ax,RC_MCBERROR
  1387.         jmp     short swapout_error
  1388. ;
  1389. swm_mcberr_ems:
  1390.         mov     dx,prep.handle          ; EMS handle
  1391.         mov     ah,45h                  ; release EMS pages on error
  1392.         int     EMM_INT
  1393.         mov     ax,RC_MCBERROR
  1394.         jmp     short swapout_error
  1395. ;
  1396. swm_mcberr_file:
  1397.         mov     ah,3eh                  ; close file
  1398.         mov     bx,prep.handle
  1399.         int     21h
  1400.         mov     dx,offset prep.swapfilename
  1401.         mov     ah,41h                  ; delete file
  1402.         int     21h
  1403.         mov     ax,RC_MCBERROR
  1404. ;
  1405. swapout_error:
  1406.         ret
  1407. ;
  1408. ;
  1409. ;       Swapout complete. Close the handle (EMS/file only),
  1410. ;       then set up low memory.
  1411. ;
  1412. swapout_complete:
  1413.         cmp     prep.swapmethod,USE_FILE
  1414.         jne     swoc_nofile
  1415. ;
  1416. ;       File swap: Close the swap file to make the handle available
  1417. ;
  1418.         mov     bx,prep.handle
  1419.         mov     ah,3eh
  1420.         int     21h                     ; close file
  1421.         mov     si,offset swapin_file
  1422.         jnc     swoc_ready
  1423.         mov     ax,RC_SWAPERROR
  1424.         jmp     swapout_error
  1425. ;
  1426. swoc_nofile:
  1427.         cmp     prep.swapmethod,USE_EMS
  1428.         jne     swoc_xms
  1429. ;
  1430. ;       EMS: Unmap page
  1431. ;
  1432.         mov     ax,4400h
  1433.         mov     bx,-1
  1434.         mov     dx,prep.handle
  1435.         int     EMM_INT
  1436.         mov     si,offset swapin_ems
  1437.         jmp     short swoc_ready
  1438. ;
  1439. swoc_xms:
  1440.         mov     si,offset swapin_xms
  1441.         jmp     short swoc_ready
  1442. ;
  1443. no_swap1:
  1444.         mov     si,offset swapin_none
  1445. ;
  1446. ;       Copy the appropriate swap-in routine to low memory.
  1447. ;
  1448. swoc_ready:
  1449.         mov     es,pspseg
  1450.         mov     cx,swap_codelen / 2
  1451.         mov     di,codebeg + base_length
  1452.         push    ds
  1453.         mov     ax,cs
  1454.         mov     ds,ax
  1455.         rep movsw
  1456. ;
  1457. ;       And while we're at it, copy the MCB allocation routine (which
  1458. ;       also includes the initial MCB release and exec call) down.
  1459. ;
  1460.         mov     cx,base_length / 2
  1461.         mov     di,param_len
  1462.         mov     si,offset lowcode_begin
  1463.         rep movsw
  1464. ;
  1465.         pop     ds
  1466.         mov     bx,es
  1467.         dec     bx
  1468.         mov     es,bx           ; let ES point to base MCB
  1469. ;
  1470. ;       Again set up the base MCB descriptor, and copy it as well as
  1471. ;       the variables set up by prep_swap to low memory.
  1472. ;       This isn't too useful if we're not swapping, but it doesn't
  1473. ;       hurt, either. The only variable used when not swapping is
  1474. ;       lprep.swapmethod.
  1475. ;
  1476.         mov     ax,es:paras
  1477.         mov     currdesc.msize,ax
  1478.         sub     ax,keep_paras
  1479.         mov     currdesc.swsize,ax
  1480.         mov     currdesc.addr,es
  1481.         mov     currdesc.swoffset,swapbeg + 16
  1482.         mov     ax,prep.total_mcbs
  1483.         mov     currdesc.num_follow,ax
  1484. ;
  1485.         mov     es,pspseg               ; ES points to PSP again
  1486. ;
  1487.         mov     cx,TYPE prep_block
  1488.         mov     si,offset prep
  1489.         mov     di,lprep
  1490.         rep movsb
  1491.         mov     cx,TYPE mcbdesc
  1492.         mov     si,offset currdesc
  1493.         mov     di,lcurrdesc
  1494.         rep movsb
  1495. ;
  1496. ;       now set up other variables in low core
  1497. ;
  1498.         mov     es:cgetmcb,getmcboff + codebeg
  1499.         mov     es:eretcode,0
  1500.         mov     es:retflags,0
  1501. ;
  1502. ;       Prepare exec parameter block
  1503. ;
  1504.         mov     ax,es
  1505.         mov     es:expar.fcb1seg,ax
  1506.         mov     es:expar.fcb2seg,ax
  1507.         mov     es:expar.pparseg,ax
  1508.         mov     es:expar.envseg,0
  1509. ;
  1510. ;       The 'zero' word is located at 80h in the PSP, the start of
  1511. ;       the command line. So as not to confuse MCB walking programs,
  1512. ;       a command line length of zero is inserted here.
  1513. ;
  1514.         mov     es:zero,0d00h           ; 00h,0dh = empty command line
  1515. ;
  1516. ;       Init default fcb's by parsing parameter string
  1517. ;
  1518.         IF      ptrsize
  1519.         lds     si,params
  1520.         ELSE
  1521.         mov     si,params
  1522.         ENDIF
  1523.         IFDEF   PASCAL
  1524.         inc     si                      ; skip length byte
  1525.         ENDIF
  1526.         push    si
  1527.         mov     di,xfcb1
  1528.         mov     es:expar.fcb1,di
  1529.         push    di
  1530.         mov     cx,16
  1531.         xor     ax,ax
  1532.         rep stosw                       ; init both fcb's to 0
  1533.         pop     di
  1534.         mov     ax,2901h
  1535.         int     21h
  1536.         mov     di,xfcb2
  1537.         mov     es:expar.fcb2,di
  1538.         mov     ax,2901h
  1539.         int     21h
  1540.         pop     si
  1541. ;
  1542. ;       move command tail string into low core
  1543. ;
  1544.         mov     di,progpars
  1545.         mov     es:expar.ppar,di
  1546.         xor     cx,cx
  1547.         inc     di
  1548. cmdcpy:
  1549.         lodsb
  1550.         or      al,al
  1551.         jz      cmdcpy_end
  1552.         stosb
  1553.         inc     cx
  1554.         jmp     cmdcpy
  1555. ;
  1556. cmdcpy_end:
  1557.         mov     al,0dh
  1558.         stosb
  1559.         mov     es:progpars,cl
  1560. ;
  1561. ;       move filename string into low core
  1562. ;
  1563.         IF      ptrsize
  1564.         lds     si,execfname
  1565.         ELSE
  1566.         mov     si,execfname
  1567.         ENDIF
  1568.         IFDEF   PASCAL
  1569.         inc     si
  1570.         ENDIF
  1571.         mov     di,filename
  1572. fncpy:
  1573.         lodsb
  1574.         stosb
  1575.         or      al,al
  1576.         jnz     fncpy
  1577. ;
  1578. ;       Setup environment copy
  1579. ;
  1580.         mov     bx,keep_paras           ; paras to keep
  1581.         mov     cx,envlen               ; environment size
  1582.         jcxz    no_environ              ; go jump if no environment
  1583.         cmp     swapping,0
  1584.         jne     do_envcopy
  1585. ;
  1586. ;       Not swapping, use the environment pointer directly.
  1587. ;       Note that the environment copy must be paragraph aligned.
  1588. ;
  1589.         IF      ptrsize
  1590.         mov     ax,word ptr (envp)+2
  1591.         mov     bx,word ptr (envp)
  1592.         ELSE
  1593.         mov     ax,ds
  1594.         mov     bx,envp
  1595.         ENDIF
  1596.         add     bx,15                   ; make sure it's paragraph aligned
  1597.         mov     cl,4
  1598.         shr     bx,cl                   ; and convert to segment addr
  1599.         add     ax,bx
  1600.         mov     es:expar.envseg,ax      ; new environment segment
  1601.         xor     cx,cx                   ; mark no copy
  1602.         xor     bx,bx                   ; and no shrink
  1603.         jmp     short no_environ
  1604. ;
  1605. ;       Swapping or EXECing without return. Set up the pointers for
  1606. ;       an environment copy (we can't do the copy yet, it might overwrite
  1607. ;       this code).
  1608. ;
  1609. do_envcopy:
  1610.         inc     cx
  1611.         shr     cx,1                    ; words to copy
  1612.         mov     ax,cx                   ; convert envsize to paras
  1613.         add     ax,7
  1614.         shr     ax,1
  1615.         shr     ax,1
  1616.         shr     ax,1
  1617.         add     bx,ax                   ; add envsize to paras to keep
  1618.         IF      ptrsize
  1619.         lds     si,envp
  1620.         ELSE
  1621.         mov     si,envp
  1622.         ENDIF
  1623. ;
  1624.         mov     ax,es                   ; low core segment
  1625.         add     ax,keep_paras           ; plus fixed paras
  1626.         mov     es:expar.envseg,ax      ; = new environment segment
  1627. ;
  1628. ;       Save stack regs, switch to local stack
  1629. ;
  1630. no_environ:
  1631.         mov     es:save_ss,ss
  1632.         mov     es:save_sp,sp
  1633.         mov     ax,es
  1634.         mov     ss,ax
  1635.         mov     sp,mystack
  1636. ;
  1637.         push    cx                      ; save env length
  1638.         push    si                      ; save env pointer
  1639.         push    ds                      ; save env segment
  1640. ;
  1641. ;       save and patch INT0 (division by zero) vector
  1642. ;
  1643.         xor     ax,ax
  1644.         mov     ds,ax
  1645.         mov     ax,word ptr ds:0
  1646.         mov     es:div0_off,ax
  1647.         mov     ax,word ptr ds:2
  1648.         mov     es:div0_seg,ax
  1649.         mov     word ptr ds:0,codebeg + iretoff
  1650.         mov     word ptr ds:2,es
  1651. ;
  1652.         pop     ds                      ; pop environment segment
  1653.         pop     si                      ; pop environment offset
  1654.         pop     cx                      ; pop environment length
  1655.         mov     di,swapbeg              ; environment destination
  1656. ;
  1657. ;       Push return address on local stack
  1658. ;
  1659.         push    cs                      ; push return segment
  1660.         mov     ax,offset exec_cont
  1661.         push    ax                      ; push return offset
  1662.         mov     es:spx,sp               ; save stack pointer
  1663. ;
  1664. ;       Goto low core code
  1665. ;
  1666.         push    es                      ; push entry segment
  1667.         mov     ax,codebeg + doexec_entry
  1668.         push    ax                      ; push entry offset
  1669. ;       ret     far                     ; can't use RET here because
  1670.         db      0cbh                    ; of .model
  1671. ;
  1672. ;----------------------------------------------------------------
  1673. ;
  1674. ;       Low core code will return to this location, with DS set to
  1675. ;       the PSP segment.
  1676. ;
  1677. exec_cont:
  1678.         push    ds
  1679.         pop     es
  1680.         mov     ss,ds:save_ss           ; reload stack
  1681.         mov     sp,ds:save_sp
  1682. ;
  1683. ;       restore INT0 (division by zero) vector
  1684. ;
  1685.         xor     cx,cx
  1686.         mov     ds,cx
  1687.         mov     cx,es:div0_off
  1688.         mov     word ptr ds:0,cx
  1689.         mov     cx,es:div0_seg
  1690.         mov     word ptr ds:2,cx
  1691. ;
  1692.         mov     ax,es:eretcode
  1693.         mov     bx,es:retflags
  1694.         mov     ds,datseg
  1695. ;
  1696. ;       Restore overwritten part of program
  1697. ;
  1698.         mov     si,offset save_dat
  1699.         mov     di,5ch
  1700.         mov     cx,savespace
  1701.         rep movsb
  1702. ;
  1703.         test    bx,1                    ; carry set?
  1704.         jnz     exec_fault              ; return EXEC error code if fault
  1705.         mov     ah,4dh                  ; else get program return code
  1706.         int     21h
  1707.         ret
  1708. ;
  1709. exec_fault:
  1710.         mov     ah,3                    ; return error as 03xx
  1711.         ret
  1712. ;
  1713. do_spawn        ENDP
  1714. ;
  1715. ;----------------------------------------------------------------------------
  1716. ;----------------------------------------------------------------------------
  1717. ;
  1718. emm_name        db      'EMMXXXX0'
  1719. ;
  1720. ;       prep_swap - prepare for swapping.
  1721. ;
  1722. ;       This routine checks all parameters necessary for swapping,
  1723. ;       and attempts to set up the swap-out area in EMS/XMS, or on file.
  1724. ;       In detail:
  1725. ;
  1726. ;            1) Check whether the do_spawn routine is located
  1727. ;               too low in memory, so it would get overwritten.
  1728. ;               If this is true, return an error code (-2).
  1729. ;
  1730. ;            2) Walk the memory control block chain, adding up the
  1731. ;               paragraphs in all blocks assigned to this process.
  1732. ;
  1733. ;            3) Check EMS (if the method parameter allows EMS):
  1734. ;               - is an EMS driver installed?
  1735. ;               - are sufficient EMS pages available?
  1736. ;               if all goes well, the EMS pages are allocated, and the
  1737. ;               routine returns success (1).
  1738. ;
  1739. ;            4) Check XMS (if the method parameter allows XMS):
  1740. ;               - is an XMS driver installed?
  1741. ;               - is a sufficient XMS block available?
  1742. ;               if all goes well, the XMS block is allocated, and the
  1743. ;               routine returns success (2).
  1744. ;
  1745. ;            5) Check file swap (if the method parameter allows it):
  1746. ;               - try to create the file
  1747. ;               - pre-allocate the file space needed by seeking to the end
  1748. ;                 and writing a byte.
  1749. ;               If the file can be written, the routine returns success (4).
  1750. ;
  1751. ;            6) Return an error code (-1).
  1752. ;
  1753.         IFDEF   PASCAL
  1754.         IFDEF   FARCALL
  1755. prep_swap       PROC    far pmethod: word, swapfname: dword
  1756.         ELSE
  1757. prep_swap       PROC    near pmethod: word, swapfname: dword
  1758.         ENDIF
  1759.         ELSE
  1760. prep_swap       PROC    uses si di,pmethod:word,swapfname:ptr byte
  1761.         ENDIF
  1762.         LOCAL   totparas: word
  1763. ;
  1764.         IFDEF   TC_HUGE
  1765.         mov     ax,SEG my_data
  1766.         mov     ds,ax
  1767.         ENDIF
  1768. ;
  1769.         IFDEF   PASCAL
  1770.         cld
  1771.         mov     ax,prefixseg
  1772.         ELSE
  1773.         IFDEF   TC_HUGE
  1774.         mov     ax,SEG _psp
  1775.         mov     es,ax
  1776.         mov     ax,es:_psp
  1777.         ELSE
  1778.         mov     ax,_psp
  1779.         ENDIF
  1780.         ENDIF
  1781. ;
  1782.         dec     ax
  1783.         mov     prep.psp_mcb,ax
  1784.         mov     prep.first_mcb,ax       ; init first MCB to PSP
  1785. ;
  1786. ;       Make a copy of the environment pointer in the PSP
  1787. ;
  1788.         inc     ax
  1789.         mov     es,ax
  1790.         mov     bx,es:psp_envptr
  1791.         dec     bx
  1792.         mov     prep.env_mcb,bx
  1793.         mov     prep.noswap_mcb,0
  1794.         test    pmethod,DONT_SWAP_ENV
  1795.         jz      can_swap_env
  1796.         mov     prep.noswap_mcb,bx
  1797. ;
  1798. ;       Check if spawn is too low in memory
  1799. ;
  1800. can_swap_env:
  1801.         mov     bx,cs
  1802.         mov     dx,offset lowcode_begin
  1803.         mov     cl,4
  1804.         shr     dx,cl
  1805.         add     bx,dx                   ; normalized start of this code
  1806.         mov     dx,keep_paras           ; the end of the modified area
  1807.         add     dx,ax                   ; plus PSP = end paragraph
  1808.         cmp     bx,dx
  1809.         ja      prepswap_ok     ; ok if start of code > end of low mem
  1810.         mov     ax,-2
  1811.         mov     prep.swapmethod,al
  1812.         ret
  1813. ;
  1814. ;       Walk the chain of memory blocks, adding up the paragraphs
  1815. ;       in all blocks belonging to this process.
  1816. ;       We try to find the first MCB by getting DOS's "list of lists",
  1817. ;       and fetching the word at offset -2 of the returned address.
  1818. ;       If this fails, we use our PSP as the starting point.
  1819. ;
  1820. prepswap_ok:
  1821.         xor     bx,bx
  1822.         mov     es,bx
  1823.         mov     ah,52h                  ; get list of lists
  1824.         int     21h
  1825.         mov     ax,es
  1826.         or      ax,bx
  1827.         jz      prep_no_first
  1828.         mov     es,es:[bx-2]            ; first MCB
  1829.         cmp     es:id,4dh               ; normal ID?
  1830.         jne     prep_no_first
  1831.         mov     prep.first_mcb,es
  1832. ;
  1833. prep_no_first:
  1834.         mov     es,prep.psp_mcb         ; ES points to base MCB
  1835.         mov     cx,es                   ; save this value
  1836.         mov     bx,es:owner             ; the current process
  1837.         mov     dx,es:paras             ; memory size in the base block
  1838.         sub     dx,keep_paras           ; minus resident paragraphs
  1839.         mov     si,0                    ; number of MCBs except base
  1840.         mov     di,prep.noswap_mcb
  1841.         mov     ax,prep.first_mcb
  1842.         mov     prep.first_mcb,0
  1843. ;
  1844. prep_mcb_walk:
  1845.         mov     es,ax
  1846.         cmp     ax,cx                   ; base block?
  1847.         je      prep_walk_next          ; then don't count again
  1848.         cmp     ax,di                   ; Non-swap MCB?
  1849.         je      prep_walk_next          ; then don't count
  1850. ;
  1851.         cmp     bx,es:owner             ; our process?
  1852.         jne     prep_walk_next          ; next if not
  1853.         inc     si
  1854.         mov     ax,es:paras             ; else get number of paragraphs
  1855.         add     ax,2                    ; + 1 for descriptor + 1 for MCB
  1856.         add     dx,ax                   ; total number of paras
  1857.         cmp     prep.first_mcb,0
  1858.         jne     prep_walk_next
  1859.         mov     prep.first_mcb,es
  1860. ;
  1861. prep_walk_next:
  1862.         cmp     es:id,4dh               ; normal block?
  1863.         jne     prep_mcb_ready          ; ready if end of chain
  1864.         mov     ax,es
  1865.         add     ax,es:paras             ; start + length
  1866.         inc     ax                      ; next MCB
  1867.         jmp     prep_mcb_walk
  1868. ;
  1869. prep_mcb_ready:
  1870.         mov     totparas,dx
  1871.         mov     prep.total_mcbs,si
  1872. ;
  1873.         test    pmethod,XMS_FIRST
  1874.         jnz     check_xms
  1875. ;
  1876. ;       Check for EMS swap
  1877. ;
  1878. check_ems:
  1879.         test    pmethod,USE_EMS
  1880.         jz      prep_no_ems
  1881. ;
  1882.         push    ds
  1883.         mov     al,EMM_INT
  1884.         mov     ah,35h
  1885.         int     21h                     ; get EMM int vector
  1886.         mov     ax,cs
  1887.         mov     ds,ax
  1888.         mov     si,offset emm_name
  1889.         mov     di,10
  1890.         mov     cx,8
  1891.         repz cmpsb                      ; EMM name present?
  1892.         pop     ds
  1893.         jnz     prep_no_ems
  1894. ;
  1895.         mov     ah,40h                  ; get EMS status
  1896.         int     EMM_INT
  1897.         or      ah,ah                   ; EMS ok?
  1898.         jnz     prep_no_ems
  1899. ;
  1900.         mov     ah,46h                  ; get EMS version
  1901.         int     EMM_INT
  1902.         or      ah,ah                   ; AH must be 0
  1903.         jnz     prep_no_ems
  1904. ;
  1905.         cmp     al,30h                  ; >= version 3.0?
  1906.         jb      prep_no_ems
  1907. ;
  1908.         mov     ah,41h                  ; Get page frame address
  1909.         int     EMM_INT
  1910.         or      ah,ah
  1911.         jnz     prep_no_ems
  1912. ;
  1913. ;       EMS present, try to allocate pages
  1914. ;
  1915.         mov     prep.ems_pageframe,bx
  1916.         mov     bx,totparas
  1917.         add     bx,ems_paramask
  1918.         mov     cl,ems_shift
  1919.         shr     bx,cl
  1920.         mov     ah,43h                  ; allocate handle and pages
  1921.         int     EMM_INT
  1922.         or      ah,ah                   ; success?
  1923.         jnz     prep_no_ems
  1924. ;
  1925. ;       EMS pages allocated, swap to EMS
  1926. ;
  1927.         mov     prep.handle,dx
  1928.         mov     ax,USE_EMS
  1929.         mov     prep.swapmethod,al
  1930.         ret
  1931. ;
  1932. ;       No EMS allowed, or EMS not present/full. Try XMS.
  1933. ;
  1934. prep_no_ems:
  1935.         test    pmethod,XMS_FIRST
  1936.         jnz     check_file              ; don't try again
  1937. ;
  1938. check_xms:
  1939.         test    pmethod,USE_XMS
  1940.         jz      prep_no_xms
  1941. ;
  1942.         mov     ax,4300h                ; check if XMM driver present
  1943.         int     2fh
  1944.         cmp     al,80h                  ; is XMM installed?
  1945.         jne     prep_no_xms
  1946.         mov     ax,4310h                ; get XMM entrypoint
  1947.         int     2fh
  1948.         mov     word ptr prep.xmm,bx    ; save entry address
  1949.         mov     word ptr prep.xmm+2,es
  1950. ;
  1951.         mov     dx,totparas
  1952.         add     dx,xms_paramask         ; round to nearest multiple of 1k
  1953.         mov     cl,xms_shift
  1954.         shr     dx,cl                   ; convert to k
  1955.         mov     ah,9                    ; allocate extended memory block
  1956.         call    prep.xmm
  1957.         or      ax,ax
  1958.         jz      prep_no_xms
  1959. ;
  1960. ;       XMS block allocated, swap to XMS
  1961. ;
  1962.         mov     prep.handle,dx
  1963.         mov     ax,USE_XMS
  1964.         mov     prep.swapmethod,al
  1965.         ret
  1966. ;
  1967. ;       No XMS allowed, or XMS not present/full. Try File swap.
  1968. ;
  1969. prep_no_xms:
  1970.         test    pmethod,XMS_FIRST
  1971.         jz      check_file
  1972.         jmp     check_ems
  1973. ;
  1974. check_file:
  1975.         test    pmethod,USE_FILE
  1976.         jnz     prep_do_file
  1977.         jmp     prep_no_file
  1978. ;
  1979. prep_do_file:
  1980.         push    ds
  1981.         IF      ptrsize
  1982.         lds     dx,swapfname
  1983.         ELSE
  1984.         mov     dx,swapfname
  1985.         ENDIF
  1986.         IFDEF   PASCAL
  1987.         inc     dx                      ; skip length byte
  1988.         ENDIF
  1989.         mov     cx,2                    ; hidden attribute
  1990.         test    pmethod,HIDE_FILE
  1991.         jnz     prep_hide
  1992.         xor     cx,cx                   ; normal attribute
  1993. ;
  1994. prep_hide:
  1995.         mov     ah,3ch                  ; create file
  1996.         test    pmethod,CREAT_TEMP
  1997.         jz      prep_no_temp
  1998.         mov     ah,5ah
  1999. ;
  2000. prep_no_temp:
  2001.         int     21h                     ; create/create temp
  2002.         jnc     prep_got_file
  2003.         jmp     prep_no_file
  2004. ;
  2005. prep_got_file:
  2006.         mov     bx,ax                   ; handle
  2007. ;
  2008. ;       save the file name
  2009. ;
  2010.         pop     es
  2011.         push    es
  2012.         mov     di,offset prep.swapfilename
  2013.         mov     cx,81
  2014.         mov     si,dx
  2015.         rep movsb
  2016. ;
  2017.         pop     ds
  2018.         mov     prep.handle,bx
  2019. ;
  2020. ;       preallocate the file
  2021. ;
  2022.         test    pmethod,NO_PREALLOC
  2023.         jnz     prep_noprealloc
  2024.         test    pmethod,CHECK_NET
  2025.         jz      prep_nonetcheck
  2026. ;
  2027. ;       check whether file is on a network drive, and don't preallocate
  2028. ;       if so. preallocation can slow down swapping significantly when
  2029. ;       running on certain networks (Novell)
  2030. ;
  2031.         mov     ax,440ah        ; check if handle is remote
  2032.         int     21h
  2033.         jc      prep_nonetcheck ; assume not remote if function fails
  2034.         test    dh,80h          ; DX bit 15 set ?
  2035.         jnz     prep_noprealloc ; remote if yes
  2036. ;
  2037. prep_nonetcheck:
  2038.         mov     dx,totparas
  2039.         mov     cl,4
  2040.         rol     dx,cl
  2041.         mov     cx,dx
  2042.         and     dx,0fff0h
  2043.         and     cx,0000fh
  2044.         sub     dx,1
  2045.         sbb     cx,0
  2046.         mov     si,dx                   ; save
  2047.         mov     ax,4200h                ; move file pointer, absolute
  2048.         int     21h
  2049.         jc      prep_file_err
  2050.         cmp     dx,cx
  2051.         jne     prep_file_err
  2052.         cmp     ax,si
  2053.         jne     prep_file_err
  2054.         mov     cx,1                    ; write 1 byte
  2055.         mov     ah,40h
  2056.         int     21h
  2057.         jc      prep_file_err
  2058.         cmp     ax,cx
  2059.         jne     prep_file_err
  2060. ;
  2061.         mov     ax,4200h                ; move file pointer, absolute
  2062.         xor     dx,dx
  2063.         xor     cx,cx                   ; rewind to beginning
  2064.         int     21h
  2065.         jc      prep_file_err
  2066. ;
  2067. prep_noprealloc:
  2068.         mov     ax,USE_FILE
  2069.         mov     prep.swapmethod,al
  2070.         ret
  2071. ;
  2072. prep_file_err:
  2073.         mov     ah,3eh                  ; close file
  2074.         int     21h
  2075.         mov     dx,offset prep.swapfilename
  2076.         mov     ah,41h                  ; delete file
  2077.         int     21h
  2078. ;
  2079. prep_no_file:
  2080.         mov     ax,-1
  2081.         mov     prep.swapmethod,al
  2082.         ret
  2083. ;
  2084. prep_swap       endp
  2085. ;
  2086.         end
  2087.  
  2088.